import logging
import pylons.decorators
from routes import url_for
import sqlalchemy.exceptions

from tw.mods import pylonshf

from cibynet.lib.base import *
from cibynet.lib.auth import authorisation

from cibynet.model import utils as m_utils
from cibynet.model.m_users import UserUpdateValidator
from cibynet.model.twforms import user_new_form, user_update_form
from cibynet.model.twforms import add_group_form, remove_group_form

log = logging.getLogger(__name__)


def _get_state():
  """Get a State object for tw.forms...
  Used in the update method of StudentsController.
  Needed since Pylons 0.9.7.rc3, pylons.c was enough at 0.9.7.rc2.
  XXX: this is very uggly...
  """
  state = type('State', (object,),
               {'_':staticmethod(pylons.i18n._)})
  state.id = request.environ['pylons.routes_dict']['id']
  return state


class StudentsController(AdminController):
  """REST Controller styled on the Atom Publishing Protocol"""
  
  #@authorisation.UserInGroupD("admin")
  def index(self, format='html'):
    """GET /students: All items in the collection."""
    if self.ajax_request:
      row = request.GET.get('row')
      student = m_utils.GetOneObject(User, id=int(row))
      return render_def('/students/index.mako', 'line_for_student_inside', 
                        student=student)
    building = request.GET.get("building")
    active = {'true': True, 'false': False, 'all': None}.\
          get(request.GET.get("active", "true").lower(), True)
    c.cache_key = "students_index_%s_%s" % (building, active)
    sq = meta.Session.query(User)
    if active is not None:
      sq = sq.filter_by(active=active)
    if building is not None:
      sq = sq.join(User.roomassociations).filter(RoomUser.to_date==None).\
          join(RoomUser.room)
      if building.isdigit():
        sq = sq.filter(Room.building_id==building)
      else:
        sq = sq.join(Room.building).filter(Building.label==building)
    # don't actually run the query here! The template is cached!
    c.students_q = sq
    return render('/students/index.mako')

  @pylonshf.validate(form=user_new_form, error_handler="new")
  def create(self):
    """POST /students: Create a new item."""
    student = m_users.User(**self.form_result)
    meta.Session.save(student)
    meta.Session.commit()
    self._invalidateIndexCache()
    return "Creating student..." + str(self.form_result)

  def new(self, format='html'):
    """GET /students/new: Form to create a new item."""
    return render('/students/new.mako')

  @pylonshf.validate(form=user_update_form, error_handler="edit", 
                     state_factory=_get_state)
  def update(self, id):
    """PUT /students/id: Update an existing item."""
    s = m_utils.GetOneObject(User, id=id)
    if not s: abort(404)
    if self.form_result["password"] == "":
      self.form_result.pop("password")
    try:
      s.update(self.form_result)
      meta.Session.commit()
      self._invalidateIndexCache(student=s)
    except sqlalchemy.exceptions.IntegrityError:
      # Race condition on the username probably
      # likely will not happen very often but just in case:
      # TODO: make a nice error feedback return to user.
      meta.Session.rollback()
      raise
    if self.ajax_request:
      return ""
    redirect_to(action='show', id=id)

  def delete(self, id):
    """DELETE /students/id: Delete an existing item."""
    user = m_utils.GetOneObject(User, id=id)
    if not user: abort(404)
    del user
    #user.active = False
    redirect_to(action='show', id=id)

  def show(self, id, format='html'):
    """GET /students/id: Show a specific item."""
    student = m_utils.GetOneObject(User, id=id)
    if student is None:
      abort(404)
    if self.ajax_request:
      section = request.GET.get("section")
      if section not in ["general_info"]:
        section = "showstudent"
      return render_def('/students/detail.mako', section, student=student)
    c.student = student
    return render('/students/detail.mako')

  def edit(self, id, format='html'):
    """GET /students/id/edit: Form to edit an existing item."""
    students_q = meta.Session.query(User)
    c.student = students_q.filter(User.id == id).one()
    if self.ajax_request:
      return render_def('/students/edit.mako', 'editform', student=c.student)
    return render('/students/edit.mako')

  @pylons.decorators.jsonify
  def validate_username(self, id):
    """Validate the username for a given id of user.
    If the username has already been attributed (and not to the same user)
    then returns False, otherwise returns True.
    """
    username = unicode(request.GET.get("username", ""))
    if not username:
      return False
    existing_ids = m_utils.GetAllIdsObject(User, username=username)
    if existing_ids and int(id) not in existing_ids:
      return False
    return True
  
  def _invalidateIndexCache(self, student=None):
    """Clear the cache of the students index page.
    """
    keys = []
    for id_building in m_utils.GetAllIdsObject(Building) + [None]:
      keys.append("students_index_%s_%s" % (id_building, True))
      keys.append("students_index_%s_%s" % (id_building, False))
      keys.append("students_index_%s_%s" % (id_building, None))
    template = get_template("/students/index.mako")
    for key in keys:
      template.cache.invalidate(key)
    if student:
      template.cache.invalidate("student_%i" % student.id)
      
  def _change_group(self, studentid):
    """Returns Student, Group, and ajax (bool).
    Intended to be call by add_group or remove_group.
    """
    group_name = self.form_result.get("group")
    group = m_utils.GetOneObject(Group, name=group_name)
    student = m_utils.GetOneObject(User, id=studentid)
    if student is None or group is None:
      abort(404)
    return student, group

  @authorisation.UserInGroupD("superadmin")
  @pylonshf.validate(form=add_group_form, error_handler="view")
  def add_group(self, id):
    """Add the user to the group 'admin' or 'superadmin'.
    """
    student, group = self._change_group(id)
    if not group in student.groups:
      student.groups.append(group)
    meta.Session.commit()
    self._invalidateIndexCache(student)
    if self.ajax_request:
      return render_def('/students/detail.mako', 'authorization_box', 
                        student=student)
    redirect_to(action='show', id=id)   
    
  @authorisation.UserInGroupD("superadmin")
  @pylonshf.validate(form=remove_group_form, error_handler="view")
  def remove_group(self, id):
    """Remove the user to the group 'admin' or 'superadmin'.
    """
    student, group = self._change_group(id)
    if group in student.groups:
      g_assoc = m_utils.GetOneObject(UserGroup, user=student, group=group)
      meta.Session.delete(g_assoc)
    meta.Session.commit()
    self._invalidateIndexCache(student)
    if self.ajax_request:
      return render_def('/students/detail.mako', 'authorization_box', 
                        student=student)
    redirect_to(action='show', id=id)
    
